<?php
// +----------------------------------------------------------------------
// | QSCMS
// +----------------------------------------------------------------------
// | Copyright (c)  2025 https://www.qqss.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed QSCMS is not free software, commercial use must purchase official license from official website to avoid unnecessary legal disputes.
// +----------------------------------------------------------------------
// | Author: Contract 990504246@qq.com
// +----------------------------------------------------------------------

namespace app\command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use app\common\plugin\HookRegistry;
use app\common\plugin\PluginManager;

/**
 * HookRegistry 内存泄露测试命令
 * 使用方法：php webman test:hook-memory --cycles=1000 --plugins=blog_comment,advertisement
 */
class TestHookMemory extends Command
{
    protected static $defaultName = 'test:hook-memory';

    protected function configure()
    {
        $this->setDescription('测试HookRegistry内存泄露问题')
            ->addOption('cycles', 'c', InputOption::VALUE_OPTIONAL, '测试循环次数', 1000)
            ->addOption('plugins', 'p', InputOption::VALUE_OPTIONAL, '测试插件列表(逗号分隔)', 'blog_comment,advertisement')
            ->addOption('interval', 'i', InputOption::VALUE_OPTIONAL, '报告间隔', 100)
            ->addOption('detail', 'd', InputOption::VALUE_NONE, '显示详细信息');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $cycles     = (int) $input->getOption('cycles');
        $pluginsStr = $input->getOption('plugins');
        $interval   = (int) $input->getOption('interval');
        $showDetail = $input->getOption('detail');

        $plugins = array_filter(array_map('trim', explode(',', $pluginsStr)));

        $output->writeln("<info>=== HookRegistry 内存泄露测试 ===</info>");
        $output->writeln("测试循环次数: {$cycles}");
        $output->writeln("测试插件: " . implode(', ', $plugins));
        $output->writeln("报告间隔: {$interval}");
        $output->writeln("");

        // 记录初始状态
        $initialMemory = memory_get_usage(true);
        $initialPeak   = memory_get_peak_usage(true);
        $initialStats  = HookRegistry::getStats();

        $output->writeln("<comment>初始状态:</comment>");
        $output->writeln("内存使用: " . $this->formatBytes($initialMemory));
        $output->writeln("内存峰值: " . $this->formatBytes($initialPeak));
        $output->writeln("钩子事件数: " . $initialStats['total_events']);
        $output->writeln("监听器数量: " . $initialStats['total_listeners']);
        $output->writeln("");

        // 开始测试循环
        $pluginManager = new PluginManager();
        $memoryHistory = [];
        $startTime     = microtime(true);

        for ($i = 1; $i <= $cycles; $i++) {
            // 随机选择一个插件进行启用/禁用操作
            $plugin = $plugins[array_rand($plugins)];

            try {
                // 模拟插件启用
                $pluginManager->enablePlugin($plugin);

                // 执行一些钩子操作
                HookRegistry::executeHook('test_hook_' . $i, ['data' => "test_data_{$i}"]);
                HookRegistry::executeHook('template_head', '');
                HookRegistry::executeHook('common_header_after', '');

                // 模拟插件禁用
                $pluginManager->disablePlugin($plugin);

            } catch (\Exception $e) {
                if ($showDetail) {
                    $output->writeln("<error>第{$i}次操作失败: " . $e->getMessage() . "</error>");
                }
            }

            // 定期报告内存使用情况
            if ($i % $interval == 0) {
                $currentMemory = memory_get_usage(true);
                $currentPeak   = memory_get_peak_usage(true);
                $currentStats  = HookRegistry::getStats();

                $memoryIncrease = $currentMemory - $initialMemory;
                $peakIncrease   = $currentPeak - $initialPeak;

                $memoryHistory[] = [
                    'cycle'     => $i,
                    'memory'    => $currentMemory,
                    'peak'      => $currentPeak,
                    'increase'  => $memoryIncrease,
                    'events'    => $currentStats['total_events'],
                    'listeners' => $currentStats['total_listeners']
                ];

                $output->writeln("<info>第{$i}次循环:</info>");
                $output->writeln("  当前内存: " . $this->formatBytes($currentMemory) .
                    " (增长: " . $this->formatBytes($memoryIncrease) . ")");
                $output->writeln("  峰值内存: " . $this->formatBytes($currentPeak) .
                    " (增长: " . $this->formatBytes($peakIncrease) . ")");
                $output->writeln("  钩子事件: " . $currentStats['total_events']);
                $output->writeln("  监听器数: " . $currentStats['total_listeners']);

                // 检查是否存在明显的内存泄露
                if ($memoryIncrease > 10 * 1024 * 1024) { // 10MB
                    $output->writeln("<error>⚠️  警告: 内存增长超过10MB，可能存在内存泄露!</error>");
                }

                $output->writeln("");
            }
        }

        // 最终统计
        $endTime     = microtime(true);
        $finalMemory = memory_get_usage(true);
        $finalPeak   = memory_get_peak_usage(true);
        $finalStats  = HookRegistry::getStats();

        $totalTime           = $endTime - $startTime;
        $totalMemoryIncrease = $finalMemory - $initialMemory;
        $totalPeakIncrease   = $finalPeak - $initialPeak;

        $output->writeln("<info>=== 测试完成 ===</info>");
        $output->writeln("总耗时: " . round($totalTime, 2) . "秒");
        $output->writeln("平均每次: " . round(($totalTime / $cycles) * 1000, 2) . "毫秒");
        $output->writeln("");

        $output->writeln("<comment>内存统计:</comment>");
        $output->writeln("初始内存: " . $this->formatBytes($initialMemory));
        $output->writeln("最终内存: " . $this->formatBytes($finalMemory));
        $output->writeln("内存增长: " . $this->formatBytes($totalMemoryIncrease));
        $output->writeln("增长率: " . round(($totalMemoryIncrease / $initialMemory) * 100, 2) . "%");
        $output->writeln("");

        $output->writeln("初始峰值: " . $this->formatBytes($initialPeak));
        $output->writeln("最终峰值: " . $this->formatBytes($finalPeak));
        $output->writeln("峰值增长: " . $this->formatBytes($totalPeakIncrease));
        $output->writeln("");

        $output->writeln("<comment>钩子统计:</comment>");
        $output->writeln("初始事件数: " . $initialStats['total_events']);
        $output->writeln("最终事件数: " . $finalStats['total_events']);
        $output->writeln("初始监听器: " . $initialStats['total_listeners']);
        $output->writeln("最终监听器: " . $finalStats['total_listeners']);
        $output->writeln("");

        // 分析内存增长趋势
        $this->analyzeMemoryTrend($output, $memoryHistory);

        // 判断测试结果
        $leakThreshold = 5 * 1024 * 1024; // 5MB
        if ($totalMemoryIncrease > $leakThreshold) {
            $output->writeln("<error>❌ 测试失败: 存在内存泄露 (增长超过5MB)</error>");
            return Command::FAILURE;
        } else {
            $output->writeln("<info>✅ 测试通过: 内存使用正常</info>");
            return Command::SUCCESS;
        }
    }

    /**
     * 分析内存增长趋势
     */
    private function analyzeMemoryTrend(OutputInterface $output, array $memoryHistory)
    {
        if (count($memoryHistory) < 2) {
            return;
        }

        $output->writeln("<comment>内存增长趋势分析:</comment>");

        $first = $memoryHistory[0];
        $last  = end($memoryHistory);

        $cycleIncrease  = $last['cycle'] - $first['cycle'];
        $memoryIncrease = $last['memory'] - $first['memory'];

        if ($cycleIncrease > 0) {
            $avgIncreasePerCycle = $memoryIncrease / $cycleIncrease;
            $output->writeln("平均每次循环内存增长: " . $this->formatBytes($avgIncreasePerCycle));

            if ($avgIncreasePerCycle > 1024) { // 1KB per cycle
                $output->writeln("<error>⚠️  警告: 平均每次循环内存增长过大</error>");
            }
        }

        // 检查是否呈线性增长（内存泄露的特征）
        $isLinearGrowth = true;
        $tolerance      = 0.1; // 10%容差

        for ($i = 1; $i < count($memoryHistory); $i++) {
            $prev = $memoryHistory[$i - 1];
            $curr = $memoryHistory[$i];

            $expectedIncrease = $avgIncreasePerCycle * ($curr['cycle'] - $first['cycle']);
            $actualIncrease   = $curr['memory'] - $first['memory'];

            if (abs($actualIncrease - $expectedIncrease) > $expectedIncrease * $tolerance) {
                $isLinearGrowth = false;
                break;
            }
        }

        if ($isLinearGrowth && $avgIncreasePerCycle > 512) {
            $output->writeln("<error>⚠️  检测到线性内存增长，可能存在内存泄露</error>");
        } else {
            $output->writeln("<info>✅ 内存增长模式正常</info>");
        }

        $output->writeln("");
    }

    /**
     * 格式化字节数
     */
    private function formatBytes($bytes)
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $bytes = max($bytes, 0);
        $pow   = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow   = min($pow, count($units) - 1);

        $bytes /= pow(1024, $pow);

        return round($bytes, 2) . ' ' . $units[$pow];
    }
}
